import { IQueryObject } from 'tiny-entity2/queryObject';
import { ServiceException } from './../common/excpetion/serviceException';
import { WorkflowNodeItem } from './../domain/models/workflowNodeItem';
import { Workflow } from "../domain/models/workflow";
import { BaseService, QueryParamsBase } from "./baseService";
import { ApiDataContext } from "../domain/apiDataContext";
import { Transaction } from "tiny-entity2";
import Enumerable = require('linq');
import { Path } from '../domain/models/path';

export class WorkflowService extends BaseService {
    CreateWorkflow(entity: Workflow, ctx?: ApiDataContext) {
        ctx || (ctx = new ApiDataContext());
        return Transaction(ctx, async (ctx) => {
            let has = await ctx.Workflow.First(x => x.name == $args1 && x.orgId == $args2, { $args1: entity.name, $args2: entity.orgId });
            if (has) {
                throw new ServiceException(`已经存在名称为【${entity.name}】的流程！`);
            }

            let d = new Workflow();
            d.ConverToEntity(entity);
            return ctx.Create(d);
        });
    }
    UpdateWorkflow(entity: Workflow, ctx?: ApiDataContext) {
        ctx || (ctx = new ApiDataContext());
        return Transaction(ctx, async (ctx) => {
            let has = await ctx.Workflow.First(x => x.name == $args1 && x.id != $args2 && x.orgId == $args3, { $args1: entity.name, $args2: entity.id, $args3: entity.orgId });
            if (has) {
                throw new ServiceException(`已经存在名称为【${entity.name}】的流程！`);
            }
            let d = new Workflow();
            d.ConverToEntity(entity);
            return ctx.Update(d);
        });
    }

    DeleteWorkflow(workflowId: string, ctx?: ApiDataContext) {
        ctx || (ctx = new ApiDataContext());
        return Transaction(ctx, async (ctx) => {
            let workflow = await this.GetWorkflowById(workflowId);
            for (let item of workflow.paths) {
                await ctx.Delete(item);
                if (item.condition) {
                    await ctx.Delete(item.condition);
                }
            }

            for (let item of workflow.workflowNodeItems) {
                await ctx.Delete(item);
            }

            await ctx.Delete(workflow);
        });
    }
    async GetWorkflowById(id: string) {
        return this.GetWorkflowInfoCtx({ id });
    }
    async GetWorkflowByFlowNodeId(flowNodeId: string) {
        return this.GetWorkflowInfoCtx({ flowNodeId });
    }

    private async GetWorkflowInfoCtx(params: { id?: string; flowNodeId?: string }) {
        let ctx = new ApiDataContext();
        let result: Workflow;
        if (params.id) {
            // let r1 = await ctx.Workflow
            //     .Join(ctx.Path).On((m, f) => m.id == f.workflowId)
            //     .Join(ctx.WorkflowNodeItem).On((m, f) => m.id == f.workflowId)
            //     .Where<Path>(x => x.id == $args1, { $args1: params.id },ctx.Path)
            //     .ToList();

            // let q = ctx.Workflow as IQueryObject<Workflow>;
            // if (params) {
            //     if (params.isDisable) {
            //         q = q.Where(x => x.isDisable == $args1, { $args1: params.isDisable });
            //     }
            //     if (params.name) {
            //         q = q.Where(x => x.name.indexOf($args1), { $args1: params.name });
            //     }
            // }

            let r = await ctx.Workflow.Where(x => x.id == $args1, { $args1: params.id }).ToList();
            if (r && r.length > 0) {
                r[0].paths = await ctx.Path.Where(x => x.workflowId == $args1, { $args1: params.id }).ToList();
                r[0].workflowNodeItems = await ctx.WorkflowNodeItem.Where(x => x.workflowId == $args1, { $args1: params.id }).ToList();
            }
            result = r.length ? r[0] : null;
        }
        else if (params.flowNodeId) {
            let r = await ctx.Workflow
                .Join(ctx.Path).On((m, f) => m.id == f.workflowId)
                .Join(ctx.WorkflowNodeItem).On((m, f) => m.id == f.workflowId)
                .Where<WorkflowNodeItem>(x => x.flowNodeId == $args1, { $args1: params.flowNodeId }, ctx.WorkflowNodeItem)
                .ToList();

            result = r.length ? r[0] : null;
        }

        if (result && result.paths && result.paths.length) {
            let conditions = await ctx.Condition.Contains(x => x.objectId, result.paths.map(x => x.id)).ToList();
            for (let item of result.paths) {
                item.condition = conditions.find(x => x.objectId == item.id);
            }
        }

        if (result && result.workflowNodeItems.length) {
            let flowNodes = await ctx.FlowNode.Contains(x => x.id, result.workflowNodeItems.map(x => x.flowNodeId)).ToList();
            for (let item of result.workflowNodeItems) {
                item.flowNode = flowNodes.find(x => x.id == item.flowNodeId);
            }
        }

        return result;
    }

    async QueryWorkflow(params: WorkflowQueryParams) {
        let q = this.QueryWorkflowCtx(params);
        let q1 = this.QueryWorkflowCtx(params);

        if (params.countOfPage && params.pageIndex) {
            q1 = q1.Take(params.countOfPage).Skip(params.pageIndex - 1) as IQueryObject<Workflow>;

            return {
                total: await q.Count(),
                list: await q1.ToList()
            }
        }
        else {
            return q.ToList();
        }
    }

    private QueryWorkflowCtx(params: WorkflowQueryParams) {
        let ctx = new ApiDataContext();
        let q = ctx.Workflow as IQueryObject<Workflow>;
        if (params) {
            if (params.isDisable) {
                q = q.Where(x => x.isDisable == $args1, { $args1: params.isDisable });
            }
            if (params.name) {
                q = q.Where(x => x.name.indexOf($args1), { $args1: params.name });
            }
        }

        return q;
    }

    CreateWorkflowNodeItem(entity: WorkflowNodeItem, ctx?: ApiDataContext) {
        ctx || (ctx = new ApiDataContext());
        return Transaction(ctx, async (ctx) => {
            let d = new WorkflowNodeItem();
            d.ConverToEntity(entity);
            return ctx.Create(d);
        });
    }
    CreateWorkflowNodeItems(datas: WorkflowNodeItem[], ctx?: ApiDataContext) {
        ctx || (ctx = new ApiDataContext());
        return Transaction(ctx, async (ctx) => {
            return ctx.CreateBatch(datas.map(x => new WorkflowNodeItem().ConverToEntity<WorkflowNodeItem>(x)));
        });
    }

    DeleteWorkflowNodeItemBywId(workflowId: string, ctx?: ApiDataContext) {
        ctx || (ctx = new ApiDataContext());
        return Transaction(ctx, async (ctx) => {
            return ctx.Delete<WorkflowNodeItem>(x => x.workflowId == $args1, ctx.WorkflowNodeItem, { $args1: workflowId });
        });
    }

    DeleteWorkflowNodeItemBywIdLogic(list: WorkflowNodeItem[], ctx?: ApiDataContext) {
        ctx || (ctx = new ApiDataContext());
        return Transaction(ctx, async (ctx) => {
            for (let item of list) {
                await ctx.Delete(item);
            }
        });
    }

    async GetWorkflowFields(workflowId: string) {
        let ctx = new ApiDataContext();
        let workflowNodeItems = await ctx.WorkflowNodeItem.Where(x => x.workflowId == $args1, { $args1: workflowId }).ToList();
        let flowNodeIds = workflowNodeItems.map(x => x.flowNodeId);
        if (!flowNodeIds || !flowNodeIds.length) {
            return [];
        }
        let pages = await ctx.Page.Contains(x => x.flowNodeId, flowNodeIds).ToList();
        let pageIds = pages.map(x => x.id);
        if (!pageIds.length) {
            return [];
        }
        let pageTmpItmes = await ctx.PageTmpItem.Contains(x => x.pageId, pageIds).ToList();
        if (!pageTmpItmes || !pageTmpItmes.length) {
            return [];
        }
        let templateIds = pageTmpItmes.map(x => x.templateId);
        let tmpComItems = await ctx.TmpComItem.Contains(x => x.templateId, templateIds).ToList();

        return Enumerable.from(tmpComItems)
            .where(x => x.component.bindField != '' && x.component.bindField != null)
            .distinct(x => x.component.bindField)
            .select(x => x.component.bindField)
            .toArray();

    }
}

export interface WorkflowQueryParams extends QueryParamsBase {
    name?: string;
    isDisable?: boolean;
}